package cn.rulian.base.curd.service;

import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.rulian.base.common.dto.CommonDto;
import cn.rulian.base.common.dto.Message;
import cn.rulian.base.common.service.BaseService;
import cn.rulian.base.common.util.FileUtil;
import cn.rulian.base.common.util.PathKit;
import cn.rulian.base.common.util.xx;
import cn.rulian.base.common.vo.Result;
import cn.rulian.base.curd.bo.Curd;
import cn.rulian.base.curd.bo.CurdField;
import cn.rulian.base.curd.dao.CurdDao;
import cn.rulian.base.curd.dao.CurdFieldDao;
import cn.rulian.base.curd.vo.CurdMeta;
import cn.rulian.base.curd.vo.CurdVo;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * Curd Service
 */
@Service
@Transactional(readOnly = true)
public class CurdService extends BaseService<Curd>
{

	@Autowired
	EntityManager em;

	@Autowired
	private CurdDao dao;

	@Autowired
	private CurdFieldDao curdFieldDao;

	@Override
	protected CurdDao getDao()
	{
		return dao;
	}
		
	/**
	 * 查询
	 */
	public Result query(final CurdVo vo)
	{
		Sort sort = new Sort("desc".equals(vo.getOrder())?Direction.DESC:Direction.ASC, vo.getSort()); // 排序
		Pageable pageable = new PageRequest(vo.getPage() - 1, vo.getRows(), sort); // 分页查询条件

		//动态查询对象
		Specification<Curd> specification = new Specification<Curd>()
		{
			@Override
			public Predicate toPredicate(Root<Curd> root, CriteriaQuery<?> cq, CriteriaBuilder cb)
			{
				return buildPredicate(root, cb, vo);
			}
		};

		// 查询
		Page<Curd> page = dao.findAll(specification, pageable);
		Result result = Result.getResult(page);
		
		return result;
	}
	

	/**
	 * 构建查询条件
	 */
	private Predicate buildPredicate(Root<Curd> root, CriteriaBuilder cb, CurdVo vo)
	{
		String name = vo.getName();	//名称
		String boPath = vo.getBoPath();	//bo类全路径

		List<Predicate> predicates = new ArrayList<Predicate>();
		
		// 名称
		if(!xx.isEmpty(name))
		{
			predicates.add(cb.like(root.<String> get("name"), "%" + name + "%"));
		}

		// bo类全路径
		if(!xx.isEmpty(boPath))
		{
			predicates.add(cb.like(root.<String> get("boPath"), "%" + boPath + "%"));
		}

		return cb.and(predicates.toArray(new Predicate[predicates.size()]));
	}
	
	/**
	 * 根据类型字符串，判断该类型是否是合法普通BO
	 */
	private boolean isBo(String type)
	{
		String[] typeArr = type.split("\\.");
		if(typeArr.length == 6  && "cn".equals(typeArr[0]) && "rulian".equals(typeArr[1]) && "bo".equals(typeArr[4]))
		{
			return true;
		}
		return false;
	}
	
	/**
	 * 验证BO
	 */
	@SuppressWarnings("rawtypes")
	@Transactional
	public Message validateBo(Long id)
	{
		Message msg =new Message();
		msg.setCode(Message._ERROR);
		msg.setMsg("验证失败！！！");
		
		Curd curd = dao.findOne(id);
		if(curd == null)
		{
			msg.setOther("未找到该CURD数据，请刷新列表！");
			return msg;
		}
		
		curd.setValidated(false); //设置验证标记
		String boPath = curd.getBoPath(); //bo类全路径
		Class boClass = null;
		
		//验证bo是否是否存在
		try
		{
			boClass = Class.forName(boPath);
		}
		catch (ClassNotFoundException e)
		{
			dao.save(curd);
			msg.setOther("未找到bo类，请检查bo类路径是否正确！");
			return msg;
		}
		
		//验证bo的包路径是否合规：固定长度，固定开头，固定倒数第二个是bo包
		//cn.rulian.mackcode.hello.bo.Hello
		String[] pathArr = boPath.split("\\."); //分解bo类
		if(!(pathArr.length == 6  && "cn".equals(pathArr[0]) && "rulian".equals(pathArr[1]) && "bo".equals(pathArr[4])))
		{
			msg.setOther("bo类必须遵循格式：cn.rulian.项目名.分组名.bo.类名 <br/>例如：cn.rulian.crm.auth.bo.User");
			return msg;
		}
		
		//验证属性类型
		StringBuilder errInfo = new StringBuilder();
		Field[] fields = boClass.getDeclaredFields(); //返回所有字段，不包括继承的
		int k=0;
		for (int i = 0; i < fields.length; i++)
		{
			Field f = fields[i];
			f.setAccessible(true);
			
			
			String type_String ="java.lang.String";
			String type_Integer ="java.lang.Integer";
			String type_Byte ="java.lang.Byte";
			String type_Short ="java.lang.Short";
			String type_Long ="java.lang.Long";
			String type_Double ="java.lang.Double";
			String type_Float ="java.lang.Float";
			String type_Boolean ="java.lang.Boolean";
			String type_Date ="java.util.Date";
			String type_List ="java.util.List";

			String type = f.getType().getName();
			if(!(type_String.equals(type) 
					||type_Integer.equals(type) 
					||type_Byte.equals(type) 
					||type_Short.equals(type) 
					||type_Long.equals(type) 
					||type_Double.equals(type) 
					||type_Float.equals(type) 
					||type_Boolean.equals(type) 
					||type_Date.equals(type) 
					||type_List.equals(type) ))
			{//属性类型不在指定范围内
				if(!this.isBo(type))
				{//属性类型不是合法bo
					k++;
					errInfo.append(k+"、	属性："+f.getName()+"&nbsp;&nbsp;&nbsp;&nbsp;  类型："+type+"<br/>");
				}
			}
			
			
		}
		if(k > 0)
		{
			dao.save(curd);
			msg.setOther("bo类中存在不支持的属性类型，规则如下：<br/>"+
					"（1）不支持Java基本类型，使用【包装器类型】代替。<br/>"+
					"（2）不支持java.lang.Character。<br/>"+
					"（3）支持java.lang.String、java.util.Date。<br/>"+
					"（4）支持其他bo类，bo类规则：cn.rulian.项目名.分组名.bo.类名，例如：cn.rulian.crm.auth.bo.User。<br/><br/>"+
					"bo中以下属性类型不支持，请修改：<br/>"+
					errInfo.toString());
			return msg;
		}

		curd.setValidated(true); //设置验证标记
		dao.saveAndFlush(curd);
		
		msg.setCode(Message._SUCCESS);
		msg.setMsg("验证通过！");
		msg.setOther("恭喜您验证通过！");
		return msg;
	}
	
	
	/**
	 * 生成配置
	 * @throws Exception 
	 */
	@SuppressWarnings("rawtypes")
	@Transactional
	public Message mackConfig(Long id) throws Exception
	{
		Message msg =new Message();
		
		//验证
		Message msgValidate = this.validateBo(id);
		if(!Message._SUCCESS.equals(msgValidate.getCode()))
		{
			msg.setCode(Message._ERROR);
			msg.setMsg("操作失败！");
			msg.setOther("原因：验证未通过！<br/>" + msgValidate.getOther());
			return msg;
		}
		
		StringBuffer info = new StringBuffer(); //消息
		
		//获取BO信息
		Curd curd = dao.findOne(id);
		String boPath = curd.getBoPath(); //bo类全路径
		Class boClass =  Class.forName(boPath); //bo类
		Field[] allFields = boClass.getDeclaredFields(); //返回所有属性，不包括继承的
		boolean haveChange = false;  //配置明细是否改变。当有新建、删除时，则置为true.

		//删除过期配置明细，保留可用配置明细。（以属性名、类型共同作为判断标准）
		List<CurdField> keepList = new ArrayList<>(); //需要保留的配置明细
		List<CurdField> nowList = curdFieldDao.findByCurd_IdOrderBySn(id); //现有配置明细列表
		int maxSn = 0; //已有配置最大序号
		if(nowList.size()>0)
		{
			List<CurdField> deleteList = new ArrayList<>(); //需要删除的配置明细
			for(CurdField cf:nowList)
			{
				boolean keep =false;
				for(Field f:allFields)
				{
					if(f.getName().equals(cf.getFieldName()) && f.getType().getName().equals(cf.getFieldType()))
					{//属性名称、属性类型均匹配，则保留配置明细
						keep = true;
						break;
					}
				}
				if(keep)
				{
					keepList.add(cf);
					maxSn = cf.getSn();
				}
				else
				{
					deleteList.add(cf);
				}
			}
			//删除过期配置明细
			if(deleteList.size()>0)
			{
				curdFieldDao.delete(deleteList);
				haveChange = true;
				info.append("本次删除过期明细配置 "+ deleteList.size() + " 条。<br/>");
			}
		}
		
		//过滤所有Field，获取需要创建配置的Field列表
		List<Field> needList = new ArrayList<>(); //需要创建配置的属性列表
		String type_List ="java.util.List";
		for(Field f:allFields)
		{
			String fieldName = f.getName(); //属字名
			String fieldType = f.getType().getName(); //属性类型
			
			//忽略list
			if(type_List.equals(fieldType))
			{
				continue;
			}
			
			boolean need =true; //是否需要重新创建明细
			for(CurdField cf:keepList)
			{
				if(fieldName.equals(cf.getFieldName()) && fieldType.equals(cf.getFieldType()))
				{//属性名称、属性类型均匹配，不需要重新创建
					need = false;
					break;
				}
			}
			if(need)
			{
				needList.add(f);
			}
		}
		
		//创建新的明细配置CurdField
		if(needList.size()>0)
		{
			String type_Boolean ="java.lang.Boolean";
			
			List<CurdField> newList = new ArrayList<>();
			for (Field f:needList)
			{
				f.setAccessible(true);
				String fieldName = f.getName(); //属字名
				String fieldType = f.getType().getName(); //属性类型
				maxSn++;
				
				CurdField cf = new CurdField();
				cf.setCurd(curd);
				cf.setFieldName(fieldName); //属字名
				cf.setFieldType(fieldType); //属性类型
				cf.setSn(maxSn); //序号
				cf.setSnAdd(maxSn);
				cf.setSnUpdate(maxSn);
				cf.setSnDatail(maxSn);
				cf.setListAlign("center"); //列表：对齐方式
				cf.setListWidth(100); //列宽
				cf.setAddType("00"); //录入组件类型
				cf.setShowInList(true); //是否显示：列表
				cf.setShowInAdd(true); //是否显示：增加页面
				cf.setShowInUpdate(true); //是否显示：编辑页面
				cf.setShowInDetail(true); //是否显示：详情页面
				cf.setCanSum(false); //是否合计
				cf.setSumTitle(false); //是否是合计标题（即显示“本页合计”、“总合计”的位置）
				cf.setCanSearch(false); //是否作为查询条件
				cf.setExcelType(false); //是否作为Excel导出时的分表条件
				cf.setListSortable(false); //是否允许排序
				cf.setAddRequired(false); //是否必填
				cf.setOtherBo(false); //是否是其他bo
				if(this.isBo(fieldType))
				{
					cf.setOtherBo(true);
				}
				
				//布尔型赋默认值
				if(type_Boolean.equals(fieldType))
				{
					cf.setParameter("是");
					cf.setFalseShow("否");
					cf.setDefaultValue("true");
				}
				
				newList.add(cf);
			}
			//保存新建配置明细
			curdFieldDao.save(newList);
			haveChange = true;
			info.append("本次新建明细配置 "+ newList.size() + " 条。<br/>");
		}
		
		/*
		 * 更新CURD相关信息。
		 * 
		 */
		if(haveChange)
		{
			int fNum = allFields.length;
			
			this.windowSizeAuto(curd);  //自动计算各窗口大小
			
			curd.setValidated(true);
			curd.setBuildConfig(true);
			curd.setConfig(false);
			curd.setConfigField(false);
			dao.save(curd);
			msg.setOther("主要完成操作：<br/>更新主配置信息，目前总计配置明细 "+fNum+" 条。<br/>"+info.toString());
		}
		else
		{
			msg.setOther("bo属性未改变，未改变任何配置！！！");
		}
		msg.setMsg("操作成功！");
		return msg;
	}

	/**
	 * 自动计算各窗口大小（不保存进数据库） <br/>
	 *
	 * 关于页面的计算规则： <br/>
	 * （1）宽度：一列增加（编辑）480，一列详情430。<br/>
	 * （2）宽度：两列增加（编辑）820，两列详情720。<br/>
	 * （3）高度：增加（编辑）页面，110+42*行数。<br/>
	 * （4）高度：详情页面，110+36*行数。<br/>

	 */
	public void windowSizeAuto(Curd curd)
	{
		List<CurdField> cfList = curdFieldDao.findByCurd_IdOrderBySn(curd.getId());
		int addNum = 0; //增加页面属性数据
		int updateNum = 0; //编辑页面属性数据
		int detailNum = 0; //详情页面属性数据
		
		for(CurdField cf:cfList)
		{
			if(cf.getShowInAdd() != null && cf.getShowInAdd())
			{
				addNum++;
			}
			if(cf.getShowInUpdate() != null && cf.getShowInUpdate())
			{
				updateNum++;
			}
			if(cf.getShowInDetail() != null && cf.getShowInDetail())
			{
				detailNum++;
			}
		}
		
		//计算列数，无值时才自动计算
		Integer tdNum = curd.getTdNum(); //列数
		if(tdNum==null)
		{
			tdNum = 1;
			if(addNum > 8)
			{
				tdNum = 2; //列数
			}
		}
		
		int windowWidth = 480; //窗口宽度
		int windowWidth_detail = 430; //窗口宽度(详情)
		int rows_add = addNum; //行数
		int rows_update = updateNum; //行数
		int rows_detail = detailNum; //行数
		
		if(tdNum == 2)
		{
			windowWidth = 820; //窗口宽度
			windowWidth_detail = 720;
			rows_add = (addNum / 2) + (addNum % 2); //行数
			rows_update = (updateNum / 2) + (updateNum % 2); //行数
			rows_detail = (detailNum / 2) + (detailNum % 2); //行数
		}

		curd.setTdNum(tdNum);
		curd.setWindowSize_add(windowWidth + "x"+(42 * rows_add + 110));
		curd.setWindowSize_update(windowWidth + "x"+(42 * rows_update + 110));
		curd.setWindowSize_detail(windowWidth_detail + "x"+(36 * rows_detail + 110));
		
	}
	
	/**
	 * 根据主配置ID,获取bo的所有属性信息。<br/>
	 * 用于主配置时下拉列表数据。
	 */
	@SuppressWarnings("rawtypes")
	public List<CommonDto> getFieldList(Long id)
	{
		List<CommonDto> list = new ArrayList<>();
		try
		{
			Curd curd = dao.findOne(id);
			String boPath = curd.getBoPath(); //bo类全路径
			Class boClass =  Class.forName(boPath); //bo类
			Field[] allFields = boClass.getDeclaredFields(); //返回所有属性，不包括继承的
			for(Field f:allFields)
			{
				String fieldName = f.getName(); //属字名
				CommonDto dto = new CommonDto();
				dto.setName(fieldName);
				list.add(dto);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return list;
	}
	

	/**
	 * 生成代码
	 * @param curdId
	 * @param test 是否是生成测试代码
	 * @return
	 */
	@Transactional
	public Message mackCode(Long curdId, boolean test)
	{
		Message msg =new Message();
		
		Curd curd = dao.findOne(curdId);
		String boPath = curd.getBoPath(); //bo类全路径，例如：cn.rulian.mackcode.hello.bo.Hello
		String[] pathArr = boPath.split("\\."); //分解bo类
		String boName = pathArr[pathArr.length-1]; //bo名称
		String boName_low = boName.substring(0, 1).toLowerCase() + boName.substring(1); //类名首字母小写
		String groupName = pathArr[pathArr.length-3]; //分组名
		String projectName = pathArr[pathArr.length-4]; //项名
		
		String temp = ""; //临时标识,用于区分测试代码和正式代码
		if(test)
		{
			projectName = "tempcode"; //测试项目
			temp = "Temp";
		}

		//模板路径、生成的java代码路径、生成的页面路径
		String templatePath = PathKit.getWebRootPath() + "/src/main/resources/templates/base/curd/template";
		String javaPath = PathKit.getWebRootPath() + "/src/main/java/cn/rulian/"+projectName+"/"+groupName;
		String htmlPath = PathKit.getWebRootPath() + "/src/main/resources/templates/"+projectName+"/"+groupName;
		
		templatePath = templatePath.replaceAll("\\\\", "/"); //分隔符替换
		javaPath = javaPath.replaceAll("\\\\", "/"); //分隔符替换
		htmlPath = htmlPath.replaceAll("\\\\", "/"); //分隔符替换
		
		//需要创建的文件路径、文件
		String filePath_dao = javaPath+"/dao/"+boName+"Dao"+temp+".java";
		String filePath_service = javaPath+"/service/"+boName+"Service"+temp+".java";
		String filePath_vo = javaPath+"/vo/"+boName+"Vo.java";
		String filePath_footer = javaPath+"/vo/"+boName+"Footer.java";
		String filePath_controller = javaPath+"/controller/"+boName+"Controller"+temp+".java";
		String filePath_add = htmlPath+"/"+boName_low+"_add.html";
		String filePath_update = htmlPath+"/"+boName_low+"_update.html";
		String filePath_detail = htmlPath+"/"+boName_low+"_detail.html";
		String filePath_list = htmlPath+"/"+boName_low+"_list.html";

		File file_dao= new File(filePath_dao);
		File file_service= new File(filePath_service);
		File file_vo= new File(filePath_vo);
		File file_footer= new File(filePath_footer);
		File file_controller= new File(filePath_controller);
		File file_add= new File(filePath_add);
		File file_update= new File(filePath_update);
		File file_detail= new File(filePath_detail);
		File file_list= new File(filePath_list);

		StringBuilder info = new StringBuilder(); //消息
		if(!test)
		{//如果不是测试代码，则判断文件是否已存在，以确定是否具备创建条件
			if(file_dao.exists()) { info.append(filePath_dao+"<br/>"); }
			if(file_service.exists()) { info.append(filePath_service+"<br/>"); }
			if(file_vo.exists()) { info.append(filePath_vo+"<br/>"); }
			if(file_footer.exists()) { info.append(filePath_footer+"<br/>"); }
			if(file_controller.exists()) { info.append(filePath_controller+"<br/>"); }
			
			if(file_add.exists()) { info.append(filePath_add+"<br/>"); }
			if(file_update.exists()) { info.append(filePath_update+"<br/>"); }
			if(file_detail.exists()) { info.append(filePath_detail+"<br/>"); }
			if(file_list.exists()) { info.append(filePath_list+"<br/>"); }
			
			if(info.length() > 0)
			{
				msg.setCode(Message._ERROR);
				msg.setMsg("生成代码失败！！！");
				msg.setOther("以下文件已存在，将先手动处理后，再生成代码：<br>"+info.toString());
				return msg;
			}
		}
		
		//判断文件所在目录是否存在，如果不存在，则创建目录。（有些文件在同一个目录下，只处理一个文件即可。）
		if(!this.createFileDir(file_dao)) { info.append("dao 目录"); }
		if(!this.createFileDir(file_service)) { info.append("service 目录"); }
		if(!this.createFileDir(file_vo)) { info.append("vo 目录"); }
		if(!this.createFileDir(file_controller)) { info.append("controller 目录"); }
		if(!this.createFileDir(file_add)) { info.append("html 目录"); }
		
		if(info.length() > 0)
		{
			msg.setCode(Message._ERROR);
			msg.setMsg("生成代码失败！！！");
			msg.setOther("以下文件目录创建失败：<br>"+info.toString());
			return msg;
		}
		
		//加工html模板（注：html文件，需要区分出生成代码时使用的 freemarker标签 和 运行时使用的freemarker标签，所以模板需要二次生成）
		String templatePath_html = templatePath+"/html/"; //html原始模板路径
		String templatePath_htmlTwo = templatePath+"/htmlTwo/";  //html再加工模板路径
		
		this.rebuildHtml(templatePath_html, templatePath_htmlTwo, "add.html");
		this.rebuildHtml(templatePath_html, templatePath_htmlTwo,"detail.html");
		this.rebuildHtml(templatePath_html, templatePath_htmlTwo,"list.html");
		
		//各模板文件名
		String templateName_dao = "Dao.html";
		String templateName_service = "Service.html";
		String templateName_vo = "Vo.html";
		String templateName_footer = "Footer.html";
		String templateName_controller = "Controller.html";
		
		String templateName_add = "add.html";
		String templateName_detail = "detail.html";
		String templateName_list = "list.html";
		
		CurdMeta curdMeta = this.getCurdMeta(curdId); //元数据
		String templatePath_java = templatePath+"/java/"; //java模板路径
		
		Map<String,String> map = new HashMap<>();
		map.put("basePackage", "cn.rulian."+projectName+"."+groupName); //java代码的基础包
		map.put("boName", boName); //boName
		map.put("boName_low", boName_low); //boName
		map.put("groupName", groupName); //boName
		map.put("projectName", projectName); //boName
		
		curdMeta.setMap(map);
		curdMeta.setTemp(temp); 
		
		//生成运行文件
		this.freemarker(curdMeta, templatePath_java, templateName_dao, file_dao);
		this.freemarker(curdMeta, templatePath_java, templateName_service, file_service);
		this.freemarker(curdMeta, templatePath_java, templateName_vo, file_vo);
		this.freemarker(curdMeta, templatePath_java, templateName_footer, file_footer);
		this.freemarker(curdMeta, templatePath_java, templateName_controller, file_controller);	
		
		curdMeta.setType("add");
		curdMeta.setTempFields(curdMeta.getAddFields());
		curdMeta.setTempFields2(curdMeta.getAddFields2());
		this.freemarker(curdMeta, templatePath_htmlTwo, templateName_add, file_add);
		
		curdMeta.setType("update");
		curdMeta.setTempFields(curdMeta.getUpdateFields());
		curdMeta.setTempFields2(curdMeta.getUpdateFields2());
		this.freemarker(curdMeta, templatePath_htmlTwo, templateName_add, file_update);
		
		this.freemarker(curdMeta, templatePath_htmlTwo, templateName_detail, file_detail);
		this.freemarker(curdMeta, templatePath_htmlTwo, templateName_list, file_list);

		//更新预览地址
		if(test)
		{
			curd.setUrlTest("/"+projectName+"/"+groupName+"/"+boName_low+"/listPage");
		}
		else
		{
			curd.setUrlFinal("/"+projectName+"/"+groupName+"/"+boName_low+"/listPage");
		}
		dao.save(curd);
		
		msg.setMsg("生成代码成功！");
		return msg;
	}
	
	/**
	 * freemarker
	 */
	@SuppressWarnings("deprecation")
	private void freemarker(CurdMeta curdMeta, String tempFilePath, String tempFileName, File genFile)
	{
		Configuration config = new Configuration();
		PrintWriter pw = null;
        try{
    		config.setDirectoryForTemplateLoading(new File(tempFilePath));  
    		config.setObjectWrapper(new DefaultObjectWrapper());  
        	pw = new PrintWriter(genFile);
			Template template = config.getTemplate(tempFileName);
			template.process(curdMeta, pw);
		} 
        catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			if (pw != null)
			{
				pw.close();
			}
		}
	}
	
	 /**
	 * 创建文件所在目录
	 */
	private boolean createFileDir(File file) 
	{
		 File dir = file.getParentFile();
		 if (!dir.exists()) 
		 {  
			 if (dir.mkdirs())
			 {  
				 System.out.println("创建目录 " + dir.getPath() + " 成功！");  
				 return true;  
			 }
			 else
			 {  
				 System.out.println("创建目录 " + dir.getPath() + " 失败！");  
				 return false;  
			 }
		 }
		 return true;
	}  
	
	/**
	 * 对html模板进行再加工，得到生成代码时所需要的html模板。<br/>
	 * （1）html文件，需要区分出生成代码时使用的 freemarker标签 和 运行时使用的freemarker标签，所以模板需要二次生成。<br/>
	 * （2）在编写原始html模板时，对于运行时所需要的freemarker标签原样输出，对于生成代码时运行的标签，# 用 ## 代替， $ 用 ￥ 代替。
	 */
	private void rebuildHtml(String fromPath,String toPath,String fileName) {
		String content = FileUtil.readTxt2String(new File(fromPath + fileName));
		
		//对运行时标签进行替换，使freemark不解析
		content = content.replaceAll("\\$\\{", "\\${'\\$'}{"); //这个必须第一个执行，否则会出现二次替换
		content = content.replaceAll("<#include", "\\${\"<\"}#include");
		content = content.replaceAll("<#if", "\\${\"<\"}#if");
		content = content.replaceAll("<#else", "\\${\"<\"}#else");
		content = content.replaceAll("</#if>", "\\${\"<\"}/#if>");
		content = content.replaceAll("<#list", "\\${\"<\"}#list");
		content = content.replaceAll("</#list>", "\\${\"<\"}/#list>");
		content = content.replaceAll("<#assign", "\\${\"<\"}#assign");
		
		//对自定义标签替换，使之在生成代码时，可正常解析
		content = content.replaceAll("<##if", "<#if");
		content = content.replaceAll("<##else", "<#else");
		content = content.replaceAll("</##if>", "</#if>");
		content = content.replaceAll("<##list", "<#list");
		content = content.replaceAll("</##list>", "</#list>");
		content = content.replaceAll("<##assign", "<#assign");
		content = content.replaceAll("\\¥\\{", "\\${");
		
		FileUtil.saveToFile(toPath + fileName, content);
	}
	
	
	/**
	 * 组织CURD元数据
	 */
	private CurdMeta getCurdMeta(Long curdId)
	{
		CurdMeta meta = new CurdMeta();
		Curd curd = dao.findOne(curdId);
		meta.setCurd(curd);
		
		List<CurdField> list = curdFieldDao.findByCurd_IdOrderBySn(curdId);

		List<CurdField> queryFields = new ArrayList<>(); //查询条件
		List<CurdField> listFields = new ArrayList<>(); //列表
		List<CurdField> sumFields = new ArrayList<>(); //需要求和的属性列表
		List<CurdField> addFields = new ArrayList<>(); //增加页面
		List<CurdField> updateFields = new ArrayList<>(); //编辑页面
		List<CurdField> detailFields = new ArrayList<>(); //详情页面
		
		
		for(CurdField cf:list)
		{
			//查询条件
			if(cf.getCanSearch())
			{
				queryFields.add(cf);
			}
			
			//列表
			if(cf.getShowInList())
			{
				listFields.add(cf);
			}
			
			//增加页面
			if(cf.getShowInAdd())
			{
				addFields.add(cf);
			}
			
			//编辑页面
			if(cf.getShowInUpdate())
			{
				updateFields.add(cf);
			}
			
			//详情页面
			if(cf.getShowInDetail())
			{
				detailFields.add(cf);
			}
			
			//需要求和的属性列表
			if(cf.getCanSum())
			{
				sumFields.add(cf);
			}
		}
		
		
		//对增加、编辑、详情页面元素进行排序
		Collections.sort(addFields, new Comparator<CurdField>() {
			@Override
			public int compare(CurdField obj1, CurdField obj2) 
			{
				if(obj1.getSnAdd() == null)
				{
					return -1;
				}
				return obj1.getSnAdd().compareTo(obj2.getSnAdd());
			}
		}); 

		Collections.sort(updateFields, new Comparator<CurdField>() {
			@Override
			public int compare(CurdField obj1, CurdField obj2) 
			{
				if(obj1.getSnUpdate() == null)
				{
					return -1;
				}
				return obj1.getSnUpdate().compareTo(obj2.getSnUpdate());
			}
		}); 

		Collections.sort(detailFields, new Comparator<CurdField>() {
			@Override
			public int compare(CurdField obj1, CurdField obj2) 
			{
				if(obj1.getSnDatail() == null)
				{
					return -1;
				}
				return obj1.getSnDatail().compareTo(obj2.getSnDatail());
			}
		}); 
		
		meta.setQueryFields(queryFields);
		meta.setListFields(listFields);
		meta.setSumFields(sumFields);
		meta.setAddFields(addFields);
		meta.setUpdateFields(updateFields);
		meta.setDetailFields(detailFields);

		meta.setAddFields2(this.processList(addFields, curd.getTdNum()));
		meta.setUpdateFields2(this.processList(updateFields, curd.getTdNum()));
		meta.setDetailFields2(this.processList(detailFields, curd.getTdNum()));
		
		return meta;
	}
	
	/**
	 * 列表处理，用于加工增加、修改、详述页面中，两列时所需要的数据
	 */
	private List<List<CurdField>> processList(List<CurdField> list,int rowNum)
	{
		List<List<CurdField>> doubleList = new ArrayList<>();  //双层结果list
		if(rowNum == 2)
		{//两列
			int size = list.size(); //总数量
			int length = size/2 + size%2 ; //
			for(int i=0; i<length; i++)
			{
				List<CurdField> sonList = new ArrayList<>(); //子列表，代表一行（tr）所需数据
				sonList.add(list.get(i));
				int k = length + i;
				if(k < size)
				{
					sonList.add(list.get(k)); //折中向后取
				}
				doubleList.add(sonList);
			}
		}
		else
		{//一列
			for(CurdField one : list)
			{
				List<CurdField> sonList = new ArrayList<>();
				sonList.add(one);
				doubleList.add(sonList);
			}
		}
		
		return doubleList;
	}
	

	/**
	 * 根据curdId，得到所有配置明细
	 */
	public List<CurdField> findCurdFieldByCurdId(Long curdId)
	{
		return curdFieldDao.findByCurd_IdOrderBySn(curdId);
	}
	
	/**
	 * 保存一批配置明细
	 */
	@Transactional
	public List<CurdField> saveCurdFieldList(List<CurdField> list)
	{
		return curdFieldDao.save(list);
	}
	
	/**
	 * 删除测试文件
	 */
	@Transactional
	public Message deleteTestFile(Long curdId)
	{
		Message msg =new Message();
		
		Curd curd = dao.findOne(curdId);
		String boPath = curd.getBoPath(); //bo类全路径，例如：cn.rulian.mackcode.hello.bo.Hello
		String[] pathArr = boPath.split("\\."); //分解bo类
		String boName = pathArr[pathArr.length-1]; //bo名称
		String boName_low = boName.substring(0, 1).toLowerCase() + boName.substring(1); //类名首字母小写
		String groupName = pathArr[pathArr.length-3]; //分组名
		String projectName = pathArr[pathArr.length-4]; //项名
		
		String temp = "Temp"; //临时标识,用于区分测试代码和正式代码
		projectName = "tempcode"; //测试项目
		
		//模板路径、生成的java代码路径、生成的页面路径
		String javaPath = PathKit.getWebRootPath() + "/src/main/java/cn/rulian/"+projectName+"/"+groupName;
		String htmlPath = PathKit.getWebRootPath() + "/src/main/resources/templates/"+projectName+"/"+groupName;
		javaPath = javaPath.replaceAll("\\\\", "/"); //分隔符替换
		htmlPath = htmlPath.replaceAll("\\\\", "/"); //分隔符替换
		
		//需要创建的文件路径、文件
		String filePath_dao = javaPath+"/dao/"+boName+"Dao"+temp+".java";
		String filePath_service = javaPath+"/service/"+boName+"Service"+temp+".java";
		String filePath_vo = javaPath+"/vo/"+boName+"Vo.java";
		String filePath_footer = javaPath+"/vo/"+boName+"Footer.java";
		String filePath_controller = javaPath+"/controller/"+boName+"Controller"+temp+".java";
		String filePath_add = htmlPath+"/"+boName_low+"_add.html";
		String filePath_update = htmlPath+"/"+boName_low+"_update.html";
		String filePath_detail = htmlPath+"/"+boName_low+"_detail.html";
		String filePath_list = htmlPath+"/"+boName_low+"_list.html";

		File file_dao= new File(filePath_dao);
		File file_service= new File(filePath_service);
		File file_vo= new File(filePath_vo);
		File file_footer= new File(filePath_footer);
		File file_controller= new File(filePath_controller);
		File file_add= new File(filePath_add);
		File file_update= new File(filePath_update);
		File file_detail= new File(filePath_detail);
		File file_list= new File(filePath_list);
		
		file_dao.delete();
		file_service.delete();
		file_vo.delete();
		file_footer.delete();
		file_controller.delete();
		file_add.delete();
		file_update.delete();
		file_detail.delete();
		file_list.delete();
		
		//if(file_dao.exists()) { info.append(filePath_dao+"<br/>"); }

		curd.setUrlTest(null);
		dao.save(curd);
		
		msg.setMsg("删除成功！");
		msg.setOther("已删除测试代码！");
		return msg;
	}
}
